/****************************************************************************
 * Dirve lcd19264 , mode 8080. Tested on ABG192064C00-v1.0
 * Tested MCU MKE06Z
 * Owner : apleilx
 * 
 * Frequence : 40MHz
 * 
 * Some samble :
 * CN------------Chianese
 * Page----------The Page of LCD
 * Col-----------The col address of LCD
 * 16_16_________A char that is 16*16 dots
 * U-------------The chip select for LCD,(CS1--left / CS2--middle / CS3--right)
 * Com-----------LCD command
 * Disp----------Display
 * W-------------write
 * Rec-----------recently
 * Img-----------Image
 * Anti----------Anti-color
 * DIR-----------direction
 * DEL-----------delay
 * Explanation:
 * ANSEL must be set again
 * This file relay on ASCII_16_32.c,ASCII_8_16
 * that is in pic18f2xk22flash.h .
 * ASCII_16_32.c and ASCII_8_16 is unrelaied on hardware ,you can copy those to your project.
 * Imag.c and CN_16_16_Index.c is not necessary ,unless you want to display chianese and image.
 * **************************************************************************/

#include "system.h"
#include "lcd19264_hal.h"
#include "pincfg.h"

/**************************************************************************
 * define control bit for Lcd
 *************************************************************************/


#define LCD_BUSY_MASK                   ((unsigned char)0x80)

#define Lcd_CS1_Enable()           LCD_CS1 = 0
#define Lcd_CS1_Disable()          LCD_CS1 = 1

#define Lcd_CS2_Enable()           LCD_CS2 = 0
#define Lcd_CS2_Disable()          LCD_CS2 = 1

#define Lcd_CS3_Enable()           LCD_CS3 = 0
#define Lcd_CS3_Disable()          LCD_CS3 = 1

#define Lcd_E(x)                   LCD_E = x
#define Lcd_RW(x)                  LCD_RW = x
#define Lcd_RS(x)                  LCD_RS = x
#define Lcd_RESET(x)               LCD_RESET = x

//#define Lcd_BUSY()                 ( LCD_DATA & LCD_BUSY_MASK)     
#define Lcd_DATA_Write(Data) 	     LCD_DATA = Data

uint8_t Lcd_BUSY() {
    uint8_t StBusy;
    LCD_DATA_DIR = 0x0FF;
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    StBusy = LCD_DATA & 0x80;
    LCD_DATA_DIR = 0x00;
    return StBusy;
}

#define __nop()                    NOP();

/*****************************************************************************
 *lcd E delay , 450ns
 ****************************************************************************/
void Lcd_E_DEL() {
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
}

void Lcd_Com_DEL() {
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
}

void Lcd_Sub_DEL() {
    __nop();
    __nop();
    __nop();
    __nop();
}

/*****************************************************************************
 *lcd Ready delay , 350ns 
 ****************************************************************************/
void Lcd_Read_DEL() {
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
}

void Lcd_RESET_DEL() {
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
    __nop();
}

static unsigned char WaitTimeOutCnt;

/******************************************************************************
 * Init
 * @param Content
 *****************************************************************************/
void LcdHalInit() {
    LCD_CS1_DIR = 0;
    LCD_CS2_DIR = 0;
    LCD_CS3_DIR = 0;
    LCD_CS1 = 1;
    LCD_CS2 = 1;
    LCD_CS3 = 1;

    LCD_E_DIR = 0;
    LCD_E = 0;

    LCD_RW_DIR = 0;
    LCD_RW = 0;
    LCD_RS_DIR = 0;
    LCD_RS = 0;

    LCD_DATA_DIR = 0x0FF;

    LCD_RESET_DIR = 0;
    LCD_RESET = 0;
    Lcd_RESET_DEL();
    LCD_RESET = 1;
    Lcd_RESET_DEL();
    LCD_RESET = 0;
    Lcd_RESET_DEL();
    LCD_RESET = 1;

    LCD_DATA_DIR = 0x00;

    //0x3E - display OFF
    Lcd_WriteCom_CS1(0x3E);
    Lcd_WriteCom_CS2(0x3E);
    Lcd_WriteCom_CS3(0x3E);
    //0x3F - display ON
    Lcd_WriteCom_CS1(0x3F);
    Lcd_WriteCom_CS2(0x3F);
    Lcd_WriteCom_CS3(0x3F);
    //set start line
    Lcd_WriteCom_CS1(0xc0);
    Lcd_WriteCom_CS2(0xc0);
    Lcd_WriteCom_CS3(0xc0);

    WaitTimeOutCnt = 0;

    LcdScreenFill(0x00);
    //LcdDispAnti();
}

/******************************************************************************
 * Waiting LCD untile it is not busy
 * @param Content
 *****************************************************************************/
void Lcd_Busy() {
    unsigned short TimeOutSt;

    Lcd_RS(0); //read status reg
    Lcd_RW(1);
    Lcd_Com_DEL();
    Lcd_E(1);
    Lcd_Read_DEL();

#if LCD_ENABLE > 0 
    if (WaitTimeOutCnt < LCD_WAIT_TIME_OUT_LIMIT) {
        TimeOutSt = 0;
        while (Lcd_BUSY() != 0) {
            if ((TimeOutSt++) > LCD_WAIT_TIME_OUT_CYCLE_CNT)
                break;
        }

        if (TimeOutSt > LCD_WAIT_TIME_OUT_CYCLE_CNT)
            WaitTimeOutCnt++;
        else
            WaitTimeOutCnt = 0;
    }
#endif
    Lcd_E(0);
}

/******************************************************************************
 * Write command to lcd
 * @param Content
 *****************************************************************************/
void Lcd_ComW(unsigned char Command) {
    Lcd_Busy();
    Lcd_RS(0); //com
    Lcd_RW(0); //write
    Lcd_DATA_Write(Command);
    Lcd_Com_DEL();
    Lcd_E(1);
    Lcd_E_DEL();
    Lcd_E(0);
}

/******************************************************************************
 * Write data to lcd
 * @param Content
 *****************************************************************************/
void Lcd_DataW(unsigned char Data) {
    Lcd_Busy();
    Lcd_RS(1); //data
    Lcd_RW(0); //write
    Lcd_DATA_Write(Data);
    Lcd_Com_DEL();
    Lcd_E(1);
    Lcd_E_DEL();
    Lcd_E(0);
}

/******************************************************************************
 * Write command to CS1
 * @param Content
 *****************************************************************************/
void Lcd_WriteCom_CS1(unsigned char Content) {
    Lcd_CS2_Disable();
    Lcd_CS3_Disable();
    Lcd_CS1_Enable();

    Lcd_ComW(Content);
}

/******************************************************************************
 * Write command to CS2
 * @param Content
 *****************************************************************************/
void Lcd_WriteCom_CS2(unsigned char Content) {
    Lcd_CS1_Disable();
    Lcd_CS3_Disable();
    Lcd_CS2_Enable();

    Lcd_ComW(Content);
}

/******************************************************************************
 * Write command to CS3
 * @param Content
 *****************************************************************************/
void Lcd_WriteCom_CS3(unsigned char Content) {
    Lcd_CS1_Disable();
    Lcd_CS2_Disable();
    Lcd_CS3_Enable();

    Lcd_ComW(Content);
}

/******************************************************************************
 * Write command to CS1/CS2/CS3
 * @param Content
 *****************************************************************************/
void Lcd_WriteCom(unsigned char U, unsigned char Content) {
    if (U == 0) {
        Lcd_WriteCom_CS1(Content);
    } else if (U == 1) {
        Lcd_WriteCom_CS2(Content);
    } else {
        Lcd_WriteCom_CS3(Content);
    }
}

/******************************************************************************
 * Clear the screen
 * @param Content
 *****************************************************************************/
void LcdScreenFill(unsigned char Data) {
    unsigned char Col, Page;
    unsigned char LMR;

    for (LMR = 0; LMR < LCD_CS; LMR++) {
        Lcd_CS2_Disable();
        Lcd_CS3_Disable();
        Lcd_CS1_Disable();
        for (Page = 0; Page < 8; Page++) {
            Lcd_WriteCom(LMR, 0xB8 + Page);
            Lcd_WriteCom(LMR, 0x40);
            for (Col = 0; Col < 64; Col++) {
                Lcd_Sub_DEL();
                Lcd_DataW(Data);
            }
        }
    }
    __nop();
}
